#!/usr/bin/tclsh
#
# Low fat grid client (the light-weight http-based grid platform)
# Copyright (C) 2006-2014 Jason Brazile
# 
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 3 of the License, or (at your option)
# any later version.
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
# 
# You should have received a copy of the GNU General Public License along
# with this program; if not, see http://www.gnu.org/licenses/
# 
# @(#) $Id: $
#
#

package require http 2.3

if {[llength $argv] < 1} {puts "usage: lfg_client \[host:port\]"; exit}
set ::host [lindex $argv 0]

proc download {jobid file url} {
  set fh [open [file join $jobid $file] w]
  set fullurl [format "http://%s%s" $::host $url]
  set token [::http::geturl $fullurl -channel $fh]
  upvar #0 $token state
  if {$state(status) != "ok"} { error download $jobid $file $url }
  close $fh
}

proc job {jobrequest} {
  set lines [split $jobrequest \n]

  set ok 0
  #puts "line1: [lindex $lines 0]"
  if {[regexp {job (.*)<br>} [lindex $lines 0] dummy jobid]} {
    # puts "jobid: $jobid"
    foreach line [lrange $lines 1 end] {
      set url [lindex [split $line \"] 1]
      set f   [lindex [split $line ><] 2]
      if {([string length $url] > 0) && ([string length $f] > 0)} {
        lappend jobs($jobid) $f $url
        set infiles($f) 1
        if {"$f" == "commands"} { set ok 1 }
      }
    }
  }

  if {!$ok} { puts "not ok"; return }

  file mkdir $jobid

  if {[catch {
		foreach {f url} $jobs($jobid) { download $jobid $f $url }
        } err]} {
    catch {exec rm -rf $jobid}
    return
  }

  cd $jobid

  catch {
    file attributes commands -permissions u+rx
    exec ./commands > stdout 2> stderr
  }

  # collect list of output files
  set outfiles {}
  foreach f [glob -nocomplain *] {
    if {![info exists infiles($f)]} { lappend outfiles $f }
  }

  #puts "building upload data"
  set outputData ""
  set bound "-----NEXT_PART_[clock seconds].[pid]"

  set i 0
  foreach f $outfiles {
    set fid [open $f]
    fconfigure $fid -translation binary
    if {[catch {read $fid [file size $f]} data]} {
            # return -code error $data
            cd ..
	    catch {exec rm -rf $jobid}
            return 
    }
    close $fid

    append outputData "--$bound\r\nContent-Disposition: form-data;\
		name=\"upload$i\"; filename=\"[file tail $f]\"\r
Content-Type: application/octet-stream\r
Content-Transfer-Encoding: binary\r\n\r\n$data\r\n"
 
    incr i
  }
  if {[string length $outputData] == 0} {
    cd ..
    puts stderr "No data given to post"
    return
  }
  append outputData "--${bound}--\r\n"

  #puts "sending upload request"
  set request [list ::http::geturl \
[format http://%s%s ${::host} /bin/lfg?cmd=results&jobid=$jobid&nfiles=$i&host=$::hn] \
	-type "multipart/form-data; boundary=$bound" \
	-query $outputData]
  set token [eval $request]
  upvar #0 $token state
  if {$state(status) != "ok"} { puts stderr "upload failed" }
  #puts -nonewline $state(body)
  ::http::cleanup $token
  cd ..
  catch {exec rm -rf $jobid}
}

proc main {} {

  set ::hn [lindex [split [exec hostname] .] 0]

  set url [format "http://%s/bin/lfg?cmd=get&host=$::hn" $::host] 
  ::http::config -useragent apexgridclient

  set dirname $::hn.[clock seconds]
  catch {file mkdir $dirname}
  if {[catch {cd $dirname}]} {
    puts stderr "could not make working dir"
    exit
  }

  while {1} {
    set token [::http::geturl $url]
    upvar #0 $token state

    if {$state(status) != "ok"} {
      puts stderr "get failed"
      after 1000
      continue
    }

    set body $state(body)
    ::http::cleanup $token

    set cmd [lindex [split $body] 0]

    switch -exact -- $cmd {
      job     { job $body }
      exit    { exit }
      sleep   { after 10000 }
      default { puts "unknown command" }
    }
  }
}

main
